package com.stark.wechat.service.message;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.stark.wechat.core.Config;
import com.stark.wechat.core.EncryptionDecryptionMode;
import com.stark.wechat.core.Event;
import com.stark.wechat.core.MessageType;
import com.stark.wechat.exception.WechatException;
import com.stark.wechat.message.in.EncryptMessage;
import com.stark.wechat.message.in.*;
import com.stark.wechat.service.signature.SignatureService;
import com.stark.wechat.util.AESUtil;
import lombok.Builder;

/**
 * 消息接口
 *
 * @author <a href="mailto:mengbin@hotmail.com">Ben</a>
 * @version 1.0.0
 * @since 2024/5/23
 */
@Builder
public class MessageService {

    private static final XmlMapper XML_MAPPER = new XmlMapper();

    /**
     * 公众号配置
     */
    private Config config;

    /**
     * 解析 xml 获得消息对象
     *
     * @param xml xml 字符串
     * @return 消息对象
     */
    public com.stark.wechat.message.in.Message parseXml(String xml) {
        if (EncryptionDecryptionMode.SECURE_MODE.equals(config.getEncryptionDecryptionMode())) {
            EncryptMessage encryptMessage;
            try {
                encryptMessage = XML_MAPPER.readValue(xml, EncryptMessage.class);
            } catch (JsonProcessingException e) {
                throw new WechatException("解析消息失败", e);
            }
            String encrypt = encryptMessage.getEncrypt();
            xml = AESUtil.decrypt(config.getAppId(), config.getEncodingAESKey(), encrypt);
        }

        Class<? extends Message> clazz;
        if (xml.contains("<MsgType><![CDATA[" + MessageType.text + "]]></MsgType>")) {
            clazz = TextMessage.class;
        } else if (xml.contains("<MsgType><![CDATA[" + MessageType.image + "]]></MsgType>")) {
            clazz = ImageMessage.class;
        } else if (xml.contains("<MsgType><![CDATA[" + MessageType.voice + "]]></MsgType>")) {
            clazz = VoiceMessage.class;
        } else if (xml.contains("<MsgType><![CDATA[" + MessageType.video + "]]></MsgType>")) {
            clazz = VideoMessage.class;
        } else if (xml.contains("<MsgType><![CDATA[" + MessageType.shortvideo + "]]></MsgType>")) {
            clazz = ShortVideoMessage.class;
        } else if (xml.contains("<MsgType><![CDATA[" + MessageType.location + "]]></MsgType>")) {
            clazz = LocationMessage.class;
        } else if (xml.contains("<MsgType><![CDATA[" + MessageType.link + "]]></MsgType>")) {
            clazz = LinkMessage.class;
        } else if (xml.contains("<MsgType><![CDATA[" + MessageType.event + "]]></MsgType>")) {
            if (xml.contains("<Event><![CDATA[" + Event.subscribe + "]]></Event>")) {
                clazz = SubscribeEventMessage.class;
            } else if (xml.contains("<Event><![CDATA[" + Event.unsubscribe + "]]></Event>")) {
                clazz = UnsubscribeEventMessage.class;
            } else if (xml.contains("<Event><![CDATA[" + Event.SCAN + "]]></Event>")) {
                clazz = ScanEventMessage.class;
            } else if (xml.contains("<Event><![CDATA[" + Event.LOCATION + "]]></Event>")) {
                clazz = LocationEventMessage.class;
            } else if (xml.contains("<Event><![CDATA[" + Event.CLICK + "]]></Event>")
                    || xml.contains("<Event><![CDATA[" + Event.VIEW + "]]></Event>")) {
                clazz = MenuEventMessage.class;
            } else if (xml.contains("<Event><![CDATA[" + Event.TEMPLATESENDJOBFINISH + "]]></Event>")) {
                clazz = TemplateSendJobFinishEventMessage.class;
            } else {
                throw new WechatException("不支持的事件类型");
            }
        } else {
            throw new WechatException("不支持的消息类型");
        }
        try {
            return XML_MAPPER.readValue(xml, clazz);
        } catch (JsonProcessingException e) {
            throw new WechatException("解析消息失败", e);
        }
    }

    /**
     * 消息对象转换为 xml
     *
     * @param message 消息对象
     * @return xml 字符串
     */
    public String toXml(com.stark.wechat.message.out.Message message) {
        String xml;
        try {
            xml = XML_MAPPER.writeValueAsString(message);
        } catch (JsonProcessingException e) {
            throw new WechatException("消息序列化失败", e);
        }
        if (EncryptionDecryptionMode.SECURE_MODE.equals(config.getEncryptionDecryptionMode())) {
            xml = AESUtil.encrypt(config.getAppId(), config.getEncodingAESKey(), xml);
            long timestamp = System.currentTimeMillis();
            String nonce = AESUtil.getRandomStr();

            SignatureService signatureService = SignatureService.builder().config(config).build();
            String signature = signatureService.sign(timestamp, nonce, xml);

            com.stark.wechat.message.out.EncryptMessage encryptMessage = new com.stark.wechat.message.out.EncryptMessage()
                    .setEncrypt(xml)
                    .setMsgSignature(signature)
                    .setTimeStamp(timestamp)
                    .setNonce(nonce);
            try {
                xml = XML_MAPPER.writeValueAsString(encryptMessage);
            } catch (JsonProcessingException e) {
                throw new WechatException("消息序列化失败", e);
            }
        }
        return xml;
    }

    /**
     * 获取加密消息的加密内容 Encrypt
     *
     * @param xml 加密消息
     * @return 加密内容 Encrypt，非安全模式返回 {@literal null}
     */
    public String getEncrypt(String xml) {
        if (EncryptionDecryptionMode.SECURE_MODE.equals(config.getEncryptionDecryptionMode())) {
            EncryptMessage encryptMessage;
            try {
                encryptMessage = XML_MAPPER.readValue(xml, EncryptMessage.class);
            } catch (JsonProcessingException e) {
                throw new WechatException("解析消息失败", e);
            }
            return encryptMessage.getEncrypt();
        }
        return null;
    }

}
